package com.amu.aidemo.android.xfyun

import android.util.Base64
import android.util.Log
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import okhttp3.*
import org.json.JSONObject
import java.nio.charset.StandardCharsets
import java.security.MessageDigest
import java.text.SimpleDateFormat
import java.util.*
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec

private const val TAG = "XfyunIatClient"

/**
 * 讯飞星火语音识别客户端
 */
class XfyunIatClient(
    private val appId: String,
    private val apiKey: String,
    private val apiSecret: String
) {
    companion object {
        private const val HOST_URL = "wss://iat.xf-yun.com/v1"
        private const val FRAME_SIZE = 1280 // 每帧音频大小（40ms @ 16kHz）
        private const val FRAME_INTERVAL_MS = 40L
        private const val CONNECT_TIMEOUT = 30L // 连接超时（秒）
        private const val READ_TIMEOUT = 60L // 读取超时（秒）
        private const val WRITE_TIMEOUT = 60L // 写入超时（秒）
    }

    private var webSocket: WebSocket? = null
    private var okHttpClient: OkHttpClient? = null
    private var isConnected = false
    private var audioSeq = 0

    // 回调
    var onResult: ((text: String, isFinal: Boolean) -> Unit)? = null
    var onError: ((error: String) -> Unit)? = null
    var onConnected: (() -> Unit)? = null
    var onVadDetected: (() -> Unit)? = null  // VAD 检测到静音回调

    /**
     * 连接到讯飞服务器
     */
    suspend fun connect() = withContext(Dispatchers.IO) {
        try {
            // 打印 API Keys 信息用于调试
            
            val authUrl = getAuthUrl()
            Log.d(TAG, "Connecting to: $authUrl")

            val request = Request.Builder()
                .url(authUrl)
                .build()

            // 创建 OkHttpClient 并保持引用
            okHttpClient = OkHttpClient.Builder()
                .connectTimeout(CONNECT_TIMEOUT, java.util.concurrent.TimeUnit.SECONDS)
                .readTimeout(READ_TIMEOUT, java.util.concurrent.TimeUnit.SECONDS)
                .writeTimeout(WRITE_TIMEOUT, java.util.concurrent.TimeUnit.SECONDS)
                .pingInterval(20, java.util.concurrent.TimeUnit.SECONDS) // 心跳保活
                .retryOnConnectionFailure(true)
                .build()

            webSocket = okHttpClient?.newWebSocket(request, object : WebSocketListener() {
                override fun onOpen(webSocket: WebSocket, response: Response) {
                    Log.i(TAG, "WebSocket connected")
                    isConnected = true
                    audioSeq = 0
                    onConnected?.invoke()
                }

                override fun onMessage(webSocket: WebSocket, text: String) {
                    handleMessage(text)
                }

                override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
                    Log.e(TAG, "WebSocket error", t)
                    isConnected = false
                    
                    val errorMsg = when {
                        t is java.net.SocketException -> "网络连接断开，请检查网络"
                        t is java.net.SocketTimeoutException -> "连接超时，请重试"
                        t is javax.net.ssl.SSLException -> "SSL 连接失败"
                        response?.code == 401 -> "认证失败，请检查 API Key"
                        response?.code == 403 -> "无权限访问，请检查服务是否开通"
                        else -> "连接失败: ${t.message}"
                    }
                    
                    onError?.invoke(errorMsg)
                }

                override fun onClosed(webSocket: WebSocket, code: Int, reason: String) {
                    Log.d(TAG, "WebSocket closed: $code - $reason")
                    isConnected = false
                }
            })
        } catch (e: Exception) {
            Log.e(TAG, "Failed to connect", e)
            onError?.invoke("连接异常: ${e.message}")
        }
    }

    /**
     * 发送音频数据
     * @param audioData PCM 16kHz 16bit 单声道音频数据
     * @param isFirst 是否是第一帧
     * @param isLast 是否是最后一帧
     */
    fun sendAudio(audioData: ByteArray, isFirst: Boolean = false, isLast: Boolean = false) {
        if (!isConnected || webSocket == null) {
            if (!isLast) { // 最后一帧时不打印警告
                Log.w(TAG, "Not connected, cannot send audio")
            }
            return
        }

        try {
            audioSeq++
            val status = when {
                isFirst -> 0
                isLast -> 2
                else -> 1
            }

            val json = JSONObject().apply {
                put("header", JSONObject().apply {
                    put("app_id", appId)
                    put("status", status)
                })

                if (isFirst) {
                    // 第一帧需要发送参数
                    put("parameter", JSONObject().apply {
                        put("iat", JSONObject().apply {
                            put("domain", "slm") // 大模型
                            put("language", "zh_cn")
                            put("accent", "mandarin")
                            put("vinfo", 1)
                            put("result", JSONObject().apply {
                                put("encoding", "utf8")
                                put("compress", "raw")
                                put("format", "json")
                            })
                        })
                    })
                }

                put("payload", JSONObject().apply {
                    put("audio", JSONObject().apply {
                        put("encoding", "raw")
                        put("sample_rate", 16000)
                        put("channels", 1)
                        put("bit_depth", 16)
                        put("seq", audioSeq)
                        put("status", status)
                        put("audio", Base64.encodeToString(audioData, Base64.NO_WRAP))
                    })
                })
            }

            val message = json.toString()
            val sent = webSocket?.send(message) ?: false
            
            if (!sent) {
                Log.e(TAG, "Failed to send audio frame #$audioSeq")
                isConnected = false
                onError?.invoke("发送数据失败，连接已断开")
            } else {
                Log.v(TAG, "Sent audio frame #$audioSeq, size: ${audioData.size}, status: $status")
            }
        } catch (e: Exception) {
            Log.e(TAG, "Failed to send audio", e)
            onError?.invoke("发送音频失败: ${e.message}")
        }
    }

    /**
     * 处理服务器返回的消息
     */
    private fun handleMessage(message: String) {
        try {
            val json = JSONObject(message)
            val header = json.getJSONObject("header")
            val code = header.getInt("code")
            val status = header.getInt("status")

            if (code != 0) {
                val errorMessage = header.optString("message", "Unknown error")
                Log.e(TAG, "Server error: code=$code, message=$errorMessage")
                onError?.invoke("识别错误: $errorMessage")
                return
            }

            // 解析识别结果
            if (json.has("payload")) {
                val payload = json.getJSONObject("payload")
                if (payload.has("result")) {
                    val result = payload.getJSONObject("result")
                    val textBase64 = result.getString("text")
                    val textJson = String(Base64.decode(textBase64, Base64.NO_WRAP), StandardCharsets.UTF_8)
                    
                    val text = parseResultText(textJson)
                    val isFinal = status == 2
                    
                    Log.d(TAG, "Recognition result: $text (final=$isFinal, status=$status)")
                    onResult?.invoke(text, isFinal)
                    
                    // 当 status=2 时，表示识别结束（可能是 VAD 检测到静音）
                    if (isFinal) {
                        Log.i(TAG, "🔇 VAD detected silence or recognition completed")
                        onVadDetected?.invoke()
                    }
                }
            }
        } catch (e: Exception) {
            Log.e(TAG, "Failed to parse message", e)
        }
    }

    /**
     * 解析识别结果文本
     */
    private fun parseResultText(textJson: String): String {
        try {
            val json = JSONObject(textJson)
            val ws = json.getJSONArray("ws")
            val result = StringBuilder()

            for (i in 0 until ws.length()) {
                val wsItem = ws.getJSONObject(i)
                val cw = wsItem.getJSONArray("cw")
                for (j in 0 until cw.length()) {
                    val cwItem = cw.getJSONObject(j)
                    result.append(cwItem.getString("w"))
                }
            }

            return result.toString()
        } catch (e: Exception) {
            Log.e(TAG, "Failed to parse result text", e)
            return ""
        }
    }

    /**
     * 生成鉴权 URL
     */
    private fun getAuthUrl(): String {
        val date = getGMTDate()
        val host = "iat.xf-yun.com"
        val requestLine = "GET /v1 HTTP/1.1"

        // 1. 生成 signature_origin
        val signatureOrigin = "host: $host\ndate: $date\n$requestLine"

        // 2. 使用 HMAC-SHA256 加密
        val mac = Mac.getInstance("HmacSHA256")
        val secretKeySpec = SecretKeySpec(apiSecret.toByteArray(StandardCharsets.UTF_8), "HmacSHA256")
        mac.init(secretKeySpec)
        val signatureSha = mac.doFinal(signatureOrigin.toByteArray(StandardCharsets.UTF_8))

        // 3. Base64 编码
        val signature = Base64.encodeToString(signatureSha, Base64.NO_WRAP)

        // 4. 生成 authorization_origin
        val authorizationOrigin = "api_key=\"$apiKey\", algorithm=\"hmac-sha256\", " +
                "headers=\"host date request-line\", signature=\"$signature\""

        // 5. Base64 编码 authorization
        val authorization = Base64.encodeToString(
            authorizationOrigin.toByteArray(StandardCharsets.UTF_8),
            Base64.NO_WRAP
        )

        // 6. 拼接最终 URL
        return "$HOST_URL?authorization=$authorization&date=${java.net.URLEncoder.encode(date, "UTF-8")}&host=$host"
    }

    /**
     * 获取 GMT 格式的日期
     */
    private fun getGMTDate(): String {
        val sdf = SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss 'GMT'", Locale.US)
        sdf.timeZone = TimeZone.getTimeZone("GMT")
        return sdf.format(Date())
    }

    /**
     * 断开连接
     */
    fun disconnect() {
        try {
            isConnected = false
            webSocket?.close(1000, "Client disconnect")
            webSocket = null
            
            // 关闭 OkHttpClient 连接池
            okHttpClient?.dispatcher?.executorService?.shutdown()
            okHttpClient?.connectionPool?.evictAll()
            okHttpClient = null
            
            Log.d(TAG, "Disconnected")
        } catch (e: Exception) {
            Log.e(TAG, "Error during disconnect", e)
        }
    }
}
